package ai

import (
	"context"
	"errors"
	"fmt"
	"sync"
	"time"
	"xagent/internal/memory"
	proto "xagent/proto/v1"
)

// Error definitions
var (
	ErrInvalidExperience = errors.New("invalid experience")
	ErrInvalidMetadata   = errors.New("invalid metadata")
	ErrMissingTags       = errors.New("missing tags")
)

// Experience represents a learning experience
type Experience struct {
	ID        string
	Type      string
	Content   string
	Success   bool
	Timestamp time.Time
	Metadata  map[string]interface{}
}

// LearningMetrics tracks learning performance
type LearningMetrics struct {
	ExperiencesProcessed int
	KnowledgeTransfers   int
	ReflectionCount      int
	SuccessRate          float64
	LastUpdate           time.Time
}

// LearningManager handles AI learning capabilities
type LearningManager struct {
	memoryManager    memory.MemoryManager
	metrics          *LearningMetrics
	experienceCache  map[string]*memory.MemoryEntry
	knowledgeBase    map[string]float64
	mutex            sync.RWMutex
	reflectionPeriod time.Duration
	retentionPeriod  time.Duration
	experiences      []*Experience
}

// NewLearningManager creates a new learning manager
func NewLearningManager(memoryManager memory.MemoryManager) *LearningManager {
	return &LearningManager{
		memoryManager:    memoryManager,
		metrics:          &LearningMetrics{},
		experienceCache:  make(map[string]*memory.MemoryEntry),
		knowledgeBase:    make(map[string]float64),
		reflectionPeriod: time.Hour,      // Reflect every hour
		retentionPeriod:  24 * time.Hour, // Retain experiences for 24 hours
	}
}

// ProcessExperience processes a learning experience
func (lm *LearningManager) ProcessExperience(ctx context.Context, experience *memory.MemoryEntry) error {
	// Validate experience
	if experience == nil {
		return ErrInvalidExperience
	}

	// Extract metadata
	metadata := experience.Metadata
	if metadata == nil {
		return ErrInvalidMetadata
	}

	// Process tags
	tags := experience.Tags
	if len(tags) == 0 {
		return ErrMissingTags
	}

	// Store experience
	return lm.memoryManager.Store(ctx, experience)
}

// TransferKnowledge applies learned knowledge to new situations
func (lm *LearningManager) TransferKnowledge(ctx context.Context, task *proto.Task) (float64, error) {
	relevance := 0.0
	count := 0

	lm.mutex.RLock()
	defer lm.mutex.RUnlock()

	// Find relevant knowledge
	for key, value := range lm.knowledgeBase {
		if lm.isRelevant(key, task) {
			relevance += value
			count++
		}
	}

	if count > 0 {
		relevance /= float64(count)
		lm.metrics.KnowledgeTransfers++
	}

	return relevance, nil
}

// SelfReflect performs periodic self-reflection to improve learning
func (lm *LearningManager) SelfReflect(ctx context.Context) error {
	lm.mutex.Lock()
	defer lm.mutex.Unlock()

	// Analyze recent experiences
	successCount := 0
	totalCount := 0
	for _, exp := range lm.experienceCache {
		if exp.Metadata["success"].(bool) {
			successCount++
		}
		totalCount++
	}

	// Update success rate
	if totalCount > 0 {
		lm.metrics.SuccessRate = float64(successCount) / float64(totalCount)
	}

	// Clean up old experiences
	lm.pruneOldExperiences()

	// Optimize knowledge base
	lm.optimizeKnowledgeBase()

	// Update metrics
	lm.metrics.ReflectionCount++
	lm.metrics.LastUpdate = time.Now()

	return nil
}

// StartReflectionLoop starts periodic self-reflection
func (lm *LearningManager) StartReflectionLoop(ctx context.Context) {
	go func() {
		ticker := time.NewTicker(lm.reflectionPeriod)
		defer ticker.Stop()

		for {
			select {
			case <-ctx.Done():
				return
			case <-ticker.C:
				if err := lm.SelfReflect(ctx); err != nil {
					// Log error but continue
					fmt.Printf("Self-reflection error: %v\n", err)
				}
			}
		}
	}()
}

// Helper functions

func (lm *LearningManager) extractKnowledge(experience *memory.MemoryEntry) (map[string]float64, error) {
	knowledge := make(map[string]float64)

	// Extract key factors
	metadata := experience.GetMetadata()
	if metadata == nil {
		return nil, ErrInvalidMetadata
	}

	// Extract key factors
	success, ok := metadata["success"].(bool)
	if !ok {
		return nil, fmt.Errorf("invalid success value in metadata")
	}

	duration, ok := metadata["duration"].(time.Duration)
	if !ok {
		return nil, fmt.Errorf("invalid duration value in metadata")
	}

	complexity, ok := metadata["complexity"].(float64)
	if !ok {
		return nil, fmt.Errorf("invalid complexity value in metadata")
	}

	// Calculate knowledge value
	value := 1.0
	if success {
		// Reward faster completion of complex tasks
		value *= (1.0 + complexity) / (1.0 + float64(duration.Minutes()))
	} else {
		// Penalize failures but still learn from them
		value *= -0.5
	}

	// Associate with relevant tags
	tags := experience.GetTags()
	for _, tag := range tags {
		knowledge[tag] = value
	}

	return knowledge, nil
}

func (lm *LearningManager) updateKnowledgeBase(knowledge map[string]float64) {
	for key, value := range knowledge {
		if existing, ok := lm.knowledgeBase[key]; ok {
			// Exponential moving average
			lm.knowledgeBase[key] = 0.8*existing + 0.2*value
		} else {
			lm.knowledgeBase[key] = value
		}
	}
}

func (lm *LearningManager) isRelevant(key string, task *proto.Task) bool {
	// Check if knowledge key matches task tags
	for _, tag := range task.Tags {
		if key == tag {
			return true
		}
	}
	return false
}

func (lm *LearningManager) pruneOldExperiences() {
	now := time.Now()
	var activeExperiences []*Experience

	for _, exp := range lm.experiences {
		age := now.Sub(exp.Timestamp)
		if age < lm.retentionPeriod {
			activeExperiences = append(activeExperiences, exp)
		}
	}

	lm.experiences = activeExperiences
}

func (lm *LearningManager) optimizeKnowledgeBase() {
	// Remove knowledge with very low impact
	for key, value := range lm.knowledgeBase {
		if abs(value) < 0.1 {
			delete(lm.knowledgeBase, key)
		}
	}
}

func (lm *LearningManager) updateMetrics(success bool) {
	lm.metrics.ExperiencesProcessed++
	if success {
		total := float64(lm.metrics.ExperiencesProcessed)
		current := lm.metrics.SuccessRate * (total - 1)
		lm.metrics.SuccessRate = (current + 1) / total
	}
	lm.metrics.LastUpdate = time.Now()
}

// GetMetrics returns current learning metrics
func (lm *LearningManager) GetMetrics() *LearningMetrics {
	lm.mutex.RLock()
	defer lm.mutex.RUnlock()
	return lm.metrics
}

func abs(x float64) float64 {
	if x < 0 {
		return -x
	}
	return x
}

func calculateExperienceWeight(exp *Experience) float64 {
	// Weight experiences based on recency and success
	timeSince := time.Since(exp.Timestamp)
	recencyWeight := 1.0 / (1.0 + float64(timeSince.Hours()))

	successWeight := 0.5
	if exp.Success {
		successWeight = 1.0
	}

	return recencyWeight * successWeight
}

func (l *LearningManager) processExperience(experience *memory.MemoryEntry) error {
	metadata := experience.GetMetadata()
	if metadata == nil {
		return ErrInvalidMetadata
	}

	tags := experience.GetTags()
	if len(tags) == 0 {
		return ErrMissingTags
	}

	// 处理元数据
	if outcome, ok := metadata["outcome"].(string); ok {
		l.processOutcome(outcome)
	}

	if priority, ok := metadata["priority"].(int); ok {
		l.processPriority(priority)
	}

	if duration, ok := metadata["duration"].(time.Duration); ok {
		l.processDuration(duration)
	}

	// 处理标签
	for _, tag := range tags {
		l.processTag(tag)
	}

	return nil
}

func (l *LearningManager) processOutcome(outcome string) {
	// 实现处理 outcome 的逻辑
}

func (l *LearningManager) processPriority(priority int) {
	// 实现处理 priority 的逻辑
}

func (l *LearningManager) processDuration(duration time.Duration) {
	// 实现处理 duration 的逻辑
}

func (l *LearningManager) processTag(tag string) {
	// 实现处理 tag 的逻辑
}
